﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EffiProz.Core.Lib.Tar
{
    /**
  * Purely static structure defining our interface to the Tar Entry Header.
  *
  * The fields controlled here are fields for the individual tar file entries
  * in an archive.  There is no such thing as a Header Field at the top archive
  * level.
  * <P>
  * We use header field names as they are specified in the FreeBSD man page for
  * tar in section 5 (Solaris and Linux have no such page in section 5).
  * Where we use a constant, the constant name is just the FreeBSD field name
  * capitalized.
  * Since a single field is known as either "linkflag" or "typeflag", we are
  * going with the UStar name typeflag for this field.
  * <P>
  * We purposefully define no variable for this list of fields, since
  * we DO NOT WANT TO access or change these values, due to application
  * goals or JVM limitations:<UL>
  *   <LI>gid
  *   <LI>uid
  *   <LI>linkname
  *   <LI>magic (UStar ID),
  *   <LI>magic version
  *   <LI>group name
  *   <LI>device major num
  *   <LI>device minor num
  * </UL>
  * Our application has no use for these, or Java has no ability to
  * work with them.
  * <P>
  * This class will be very elegant when refactored as an enum with enumMap(s)
  * and using generics with auto-boxing instead of the ugly and non-validating
  * casts.
  *
  * @author Blaine Simpson (blaine dot simpson at admc dot com)
  */
    public class TarHeaderFields
    {

        public const int NAME = 1;
        public const int MODE = 2;
        public const int UID = 3;
        public const int GID = 4;
        public const int SIZE = 5;
        public const int MTIME = 6;        // (File.lastModified()|*.getTime())/1000
        public const int CHECKSUM = 7;
        public const int TYPEFLAG = 8;

        // The remaining are from UStar format:
        public const int MAGIC = 9;
        public const int UNAME = 10;
        public const int GNAME = 11;
        public const int PREFIX = 12;

        // Replace these contants with proper enum once we require Java 1.5.
        public static HashMap labels = new HashMap();    // String identifier

        // (this supplied automatically by enums)
        public static HashMap starts = new HashMap();    // Starting positions
        public static HashMap stops = new HashMap();

        // 1 PAST last position (in normal Java substring fashion).
        /* Note that (with one exception), there is always 1 byte
         * between a numeric field stop and the next start.  This is
         * because null byte must occupy the intervening position.
         * This is not true for non-numeric fields (which includes the
         * link-indicator/type-flag field, which is used as a code,
         * and is not necessarily numeric with UStar format).
         *
         * As a consequence, there may be NO DELIMITER after
         * non-numerics, which may occupy the entire field segment.
         *
         * Arg.  man page for "pax" says that both original and ustar
         * headers must be <= 100 chars. INCLUDING the trailing \0
         * character.  ???  GNU tar certainly does not honor this.
         */
        static TarHeaderFields()
        {
            labels.put((NAME), "name");
            starts.put((NAME), (0));
            stops.put((NAME), (100));
            labels.put((MODE), "mode");
            starts.put((MODE), (100));
            stops.put((MODE), (107));
            labels.put((UID), "uid");
            starts.put((UID), (108));
            stops.put((UID), (115));
            labels.put((GID), "gid");
            starts.put((GID), (116));
            stops.put((GID), (123));
            labels.put((SIZE), "size");
            starts.put((SIZE), (124));
            stops.put((SIZE), (135));
            labels.put((MTIME), "mtime");
            starts.put((MTIME), (136));
            stops.put((MTIME), (147));
            labels.put((CHECKSUM), "checksum");          // Queer terminator.

            // Pax UStore does not follow spec and delimits this field like
            // any other numeric, skipping the space byte.
            starts.put((CHECKSUM), (148));    // Special fmt.
            stops.put((CHECKSUM), (156));     // Queer terminator.
            labels.put((TYPEFLAG), "typeflag");
            starts.put((TYPEFLAG), (156));    // 1-byte CODE

            // With current version, we are never doing anything with this
            // field.  In future, we will support x and/or g type here.
            stops.put((TYPEFLAG), (157));
            labels.put((MAGIC), "magic");

            // N.b. Gnu Tar does not honor this Stop.
            starts.put((MAGIC), (257));
            stops.put((MAGIC), (263));
            labels.put((UNAME), "uname");
            starts.put((UNAME), (265));
            stops.put((UNAME), (296));
            labels.put((GNAME), "gname");
            starts.put((GNAME), (297));
            stops.put((GNAME), (328));
            labels.put((PREFIX), "prefix");
            starts.put((PREFIX), (345));
            stops.put((PREFIX), (399));
        }

        // The getters below throw RuntimExceptions instead of
        // TarMalformatExceptions because these errors indicate a dev problem,
        // not some problem with a Header, or generating or reading a Header.
        static public int getStart(int field)
        {

            object iObject = starts.get((field));

            if (iObject == null)
            {
                throw new ArgumentException(
                    RB.singleton.getString(RB.UNEXPECTED_HEADER_KEY, field));
            }

            return (int)iObject;
        }

        static public int getStop(int field)
        {

            object iObject = stops.get((field));

            if (iObject == null)
            {
                throw new ArgumentException(
                    RB.singleton.getString(RB.UNEXPECTED_HEADER_KEY, field));
            }

            return (int)iObject;
        }

        static public String toString(int field)
        {

            String s = (String)labels.get((field));

            if (s == null)
            {
                throw new ArgumentException(
                    RB.singleton.getString(RB.UNEXPECTED_HEADER_KEY, field));
            }

            return s;
        }
    }
}
